package com.geek.water.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geek.water.dto.*;
import com.geek.water.entity.Menu;
import com.geek.water.mapper.MenuMapper;
import com.geek.water.service.MenuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * @author chenmin
 * @desc 菜单服务实现类 - 支持Redis缓存
 * @date 2025/7/26
 *
 * SpringBoot + 内置缓存策略 + Redis 实现缓存策略
 * 1.缓解数据库频繁访问带来的压力
 * 2.开启缓存 @EnableCaching  + @CacheConfig  +  缓存数据 @Cacheable
 * 3.注解@Cacheable ， 工作原理
 *      3.1 创建 menu:: 为键前缀的缓存空间；
 *      3.2 以keyGenerator键规则 com.geek.water.service.impl.MenuServiceImplfindAll5，查找缓存数据，若未找到则进入数据库查询，将查询到数据存入缓存并返回给用户；
 *      3.3 下一次再查询数据，还是此键则直接从缓存中匹配返回；
 * 4.如何保证数据库 与 缓存 的一致性
 *      4.1 先更新数据，再删除缓存
 *      4.2 延迟双删机制
 */
@Slf4j
@CacheConfig(cacheNames = "menu" , keyGenerator = "keyGenerator")
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Autowired
    private MenuMapper menuMapper;

    /**
     * 组装菜单数据 - 支持缓存
     *
     * @param userId 用户ID
     * @return 菜单列表
     */
    @Cacheable(keyGenerator = "keyGenerator")
    @Transactional(readOnly = true)
    @Override
    public List<MenuDTO> findAll(Long userId) {
        log.info("=== 缓存未命中，正在查询数据库获取用户 {} 的菜单数据 ===", userId);

        try {
            List<MenuDTO> list = new ArrayList<>();

            //查询一级菜单
            log.info("=== 开始查询用户 {} 的一级菜单 ===", userId);
            List<Menu> firstMenus = menuMapper.selectFirstMenuByUid(userId);
            log.info("=== 查询到 {} 个一级菜单 ===", firstMenus.size());

            if (firstMenus.isEmpty()) {
                log.warn("=== 用户 {} 没有一级菜单权限，可能的原因：1.用户没有角色 2.角色没有菜单权限 3.菜单表数据为空 ===", userId);
                return list;
            }

            firstMenus.forEach(menu -> {
                log.info("=== 处理一级菜单: ID={}, 名称={}, 路径={} ===", menu.getId(), menu.getName(), menu.getPath());

                //组装一级菜单
                MenuDTO first = new MenuDTO();
                BeanUtils.copyProperties(menu , first);

                //查询二级菜单 - 需要检查用户权限
                log.info("=== 查询菜单 {} 的二级子菜单 ===", menu.getId());
                List<MenuDTO> children = menuMapper.selectSecondMenuByUidAndParentId(userId, menu.getId());
                log.info("=== 菜单 {} 有 {} 个二级子菜单 ===", menu.getId(), children.size());

                //添加菜单
                first.setChildren(children);
                list.add(first);
            });

            log.info("=== 数据库查询完成，用户 {} 的菜单数据已缓存，总共 {} 个一级菜单 ===", userId, list.size());
            return list;
        } catch (Exception e) {
            log.error("=== 查询用户 {} 菜单数据失败 ===", userId, e);
            throw new RuntimeException("获取菜单失败: " + e.getMessage());
        }
    }

    /**
     * 保存菜单 - 更新后清除缓存
     * @param menu 菜单实体
     * @return 是否成功
     */
    @Override
    @CacheEvict(key = "#menu.id")
    @Transactional
    public boolean save(Menu menu) {
        return super.save(menu);
    }

    /**
     * 更新菜单 - 更新后清除缓存
     * @param menu 菜单实体
     * @return 是否成功
     */
    @Override
    @CacheEvict(key = "#menu.id")
    @Transactional
    public boolean updateById(Menu menu) {
        return super.updateById(menu);
    }

}
